/*
 * Tezad - Classic puzzle game
 * Copyright 2009 Shayne Riley and Paul Maseberg
 *
 * Tezad is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tezad is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tezad.  If not, see <http://www.gnu.org/licenses/>
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * To contact the authors on questions or issues regarding the license,
 * you may send an email to <pezads at gmail dot com>
 */


#ifndef GAMEMINOSTATETEST_H_
#define GAMEMINOSTATETEST_H_

#include "GameMinoState.h"
#include <gtest/gtest.h>

namespace
{

// The fixture for these tests.
class GameMinoStateTest: public testing::Test
{
};

/**
 * Test for proper use of mino state function.
 */
TEST_F(GameMinoStateTest, Mino)
{
    int nPos = 7;
    int rows[] = {0, 1, -1, 0x7FFF, 0xFFFF, 0x7FFFFFFF, 0xFFFFFFFF};
    // We use the same values for the columns, but we rotate them around so
    // that the same values aren't used at the same time. Sure, this can be
    // done with a simple modulo division but come on, this is a unit test.
    int cols[] = {0x7FFF, 0xFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0, 1, -1};

    // Test rows and columns at the same time.
    for (int i = 0; i < nPos; ++i)
    {
        GameMinoState test = GameMinoState(rows[i], cols[i], 0);

        ASSERT_EQ(rows[i], test.row())
        << "Row should be equal to what was passed in, no matter the value.";
        ASSERT_EQ(cols[i], test.col())
        << "Column should be equal to what was passed in, no matter the value.";
        ASSERT_EQ(0, test.orientation())
        << "Orientation should not change.";
    }

    // Test the orientation separately.
    int nRots = 13;
    int rots[]    = {0, 1, 2, 3, 4, -1, -2, -3, -4, 0x7FFF, 0xFFFF, 0x7FFFFFFF,
            0x80000000};
    int rotsAns[] = {0, 1, 2, 3, 0,  1,  2,  3,  0, 3,      3,      3,
            0};

    for (int i = 0; i < nRots; ++i)
    {
        GameMinoState test = GameMinoState(0, 0, rots[i]);

        ASSERT_EQ(0, test.row())
        << "Row should not change.";
        ASSERT_EQ(0, test.col())
        << "Column should not change.";
        ASSERT_EQ(rotsAns[i], test.orientation())
        << "Orientation should have modulo divided the result. Was passed "
        << rots[i] << ", should have been " << rotsAns[i] << ", but was "
        << test.orientation() << " instead.";
    }
}

/*
 * Test the row function to ensure that it returns the correct value.
 */
TEST_F(GameMinoStateTest, Row)
{
    int nPos = 7;
    int rows[] = {0, 1, -1, 0x7FFF, 0xFFFF, 0x7FFFFFFF, 0xFFFFFFFF};

    for (int i = 0; i < nPos; ++i)
    {
        GameMinoState test = GameMinoState(rows[i], 0, 0);

        ASSERT_EQ(rows[i], test.row())
        << "Row should be equal to what was passed in, no matter the value.";
        ASSERT_EQ(0, test.col())
        << "Column should be 0.";
        ASSERT_EQ(0, test.orientation())
        << "Orientation should not change.";
    }
}

/*
 * Test the col function to ensure that it passes the correct value.
 */
TEST_F(GameMinoStateTest, Col)
{
    int nPos = 7;
    int cols[] = {0, 1, -1, 0x7FFF, 0xFFFF, 0x7FFFFFFF, 0xFFFFFFFF};

    for (int i = 0; i < nPos; ++i)
    {
        GameMinoState test = GameMinoState(0, cols[i], 0);

        ASSERT_EQ(0, test.row())
        << "Row should be 0.";
        ASSERT_EQ(cols[i], test.col())
        << "Column should be equal to what was passed in, no matter the value.";
        ASSERT_EQ(0, test.orientation())
        << "Orientation should not change.";
    }
}

/*
 * Test the orientation function to ensure that it passes the correct value.
 */
TEST_F(GameMinoStateTest, Orientation)
{
    int nRots = 13;
    int rots[]    = {0, 1, 2, 3, 4, -1, -2, -3, -4, 0x7FFF, 0xFFFF, 0x7FFFFFFF,
            0x80000000};
    int rotsAns[] = {0, 1, 2, 3, 0,  1,  2,  3,  0, 3,      3,      3,
            0};

    for (int i = 0; i < nRots; ++i)
    {
        GameMinoState test = GameMinoState(0, 0, rots[i]);

        ASSERT_EQ(0, test.row())
        << "Row should not change.";
        ASSERT_EQ(0, test.col())
        << "Column should not change.";
        ASSERT_EQ(rotsAns[i], test.orientation())
        << "Orientation should have modulo divided the result. Was passed "
        << rots[i] << ", should have been " << rotsAns[i] << ", but was "
        << test.orientation() << " instead.";
    }
}

/*
 * Behavior test for rotateCCW.
 */
TEST_F(GameMinoStateTest, rotateCCW)
{
    GameMinoState t1 = GameMinoState(0, 0, 0);

    GameMinoState result = t1.rotateCCW();
    ASSERT_EQ(3, result.orientation())
    << "The resulting orientation was not counter-clockwise.";
    ASSERT_EQ(0, result.row())
    << "rotateCCW should not change the row.";
    ASSERT_EQ(0, result.col())
    << "rotateCCW should not change the column.";

    ASSERT_EQ(0, t1.orientation())
    << "Calling rotateCCW should only create a new state and not affect "
    << "the current one.";

    ASSERT_EQ(2, t1.rotateCCW().rotateCCW().orientation());
    ASSERT_EQ(1, t1.rotateCCW().rotateCCW().rotateCCW().orientation());
    ASSERT_EQ(0, t1.rotateCCW().rotateCCW().rotateCCW().rotateCCW()
            .orientation());
}

/*
 * Behavior test for rotateCW.
 */
TEST_F(GameMinoStateTest, rotateCW)
{
    GameMinoState t1 = GameMinoState(0, 0, 0);

    GameMinoState result = t1.rotateCW();
    ASSERT_EQ(1, result.orientation())
    << "The resulting orientation was not clockwise.";
    ASSERT_EQ(0, result.row())
    << "rotateCW should not change the row.";
    ASSERT_EQ(0, result.col())
    << "rotateCW should not change the column.";

    ASSERT_EQ(0, t1.orientation())
    << "Calling rotateCW should only create a new state and not affect "
    << "the current one.";

    ASSERT_EQ(2, t1.rotateCW().rotateCW().orientation());
    ASSERT_EQ(3, t1.rotateCW().rotateCW().rotateCW().orientation());
    ASSERT_EQ(0, t1.rotateCW().rotateCW().rotateCW().rotateCW()
            .orientation());
}

/*
 * Behavior test for shiftLeft.
 */
TEST_F(GameMinoStateTest, shiftLeft)
{
    GameMinoState t1 = GameMinoState(0, 0, 0);

    GameMinoState result = t1.shiftLeft();
    ASSERT_EQ(-1, result.col())
    << "The resulting translation was not to the left.";
    ASSERT_EQ(0, result.row())
    << "shiftLeft should not change the row.";
    ASSERT_EQ(0, result.orientation())
    << "shiftLeft should not change the orientation.";

    ASSERT_EQ(0, t1.col())
    << "Calling shiftLeft should only create a new state and not affect "
    << "the current one.";

    // Corner case test. This should wrap from the left side to the right.
    GameMinoState t2 = GameMinoState(0, 0x80000000, 0);
    ASSERT_EQ(0x7FFFFFFF, t2.shiftLeft().col())
    << "Calling shiftLeft when on the lowest value for a signed 32-bit integer "
    << "should wrap the column to the highest value.";
}

/*
 * Behavior test for shiftRight.
 */
TEST_F(GameMinoStateTest, shiftRight)
{
    GameMinoState t1 = GameMinoState(0, 0, 0);

    GameMinoState result = t1.shiftRight();
    ASSERT_EQ(1, result.col())
    << "The resulting translation was not to the right.";
    ASSERT_EQ(0, result.row())
    << "shiftRight should not change the row.";
    ASSERT_EQ(0, result.orientation())
    << "shiftRight should not change the orientation.";

    ASSERT_EQ(0, t1.col())
    << "Calling shiftRight should only create a new state and not affect "
    << "the current one.";

    // Corner case test. This should wrap from the right side to the left.
    GameMinoState t2 = GameMinoState(0, 0x7FFFFFFF, 0);
    ASSERT_EQ(0x80000000, t2.shiftRight().col())
    << "Calling shiftRight when on the highest value for a signed 32-bit "
    << "integer should wrap the column to the lowest value.";
}

/*
 * Behavior test for shiftUp.
 */
TEST_F(GameMinoStateTest, shiftUp)
{
    GameMinoState t1 = GameMinoState(0, 0, 0);

    GameMinoState result = t1.shiftUp();
    ASSERT_EQ(1, result.row())
    << "The resulting translation was not up.";
    ASSERT_EQ(0, result.col())
    << "shiftUp should not change the col.";
    ASSERT_EQ(0, result.orientation())
    << "shiftUp should not change the orientation.";

    ASSERT_EQ(0, t1.col())
    << "Calling shiftUp should only create a new state and not affect "
    << "the current one.";

    // Corner case test. This should wrap from the top to the bottom.
    GameMinoState t2 = GameMinoState(0x7FFFFFFF, 0, 0);
    ASSERT_EQ(0x80000000, t2.shiftUp().row())
    << "Calling shiftUp when on the highest value for a signed 32-bit "
    << "integer should wrap the column to the lowest value.";
}

/*
 * Behavior test for shiftDown.
 */
TEST_F(GameMinoStateTest, shiftDown)
{
    GameMinoState t1 = GameMinoState(0, 0, 0);

    GameMinoState result = t1.shiftDown();
    ASSERT_EQ(-1, result.row())
    << "The resulting translation was not down.";
    ASSERT_EQ(0, result.col())
    << "shiftDown should not change the col.";
    ASSERT_EQ(0, result.orientation())
    << "shiftDown should not change the orientation.";

    ASSERT_EQ(0, t1.col())
    << "Calling shiftDown should only create a new state and not affect "
    << "the current one.";

    // Corner case test. This should wrap from the bottom to the top.
    GameMinoState t2 = GameMinoState(0x80000000, 0, 0);
    ASSERT_EQ(0x7FFFFFFF, t2.shiftDown().row())
    << "Calling shiftDown when on the highest value for a signed 32-bit "
    << "integer should wrap the column to the lowest value.";
}

} // Namespace
#endif /* GAMEMINOSTATETEST_H_ */
